/**
 * 1.使用FSM状态机解析html分别获得tag，attribute
 * 2.将收集到的tag，attribute用栈处理构建dom树
 * 3.文本处理
 */
// const cssCompute = require('./csscomputing');
// const layout = require('./layout')
let currentToken = null;
let currentAttribute = null;
let currentTextNode = null;

//使用Array栈来构建dom树
let stack;

function emit(token){
  // console.log(token)
  
  let top = stack[stack.length -1];
  //startTag时入栈
  if(token.type === "startTag"){
    //定义element
    let element = {
      type: "element",
      children: [],
      attributes:[]
    }
    element.tagName = token.tagName;
    for(let c in token){
      if(c !== "type" && c !== "tagName"){
        element.attributes.push({
          name: c,
          value: token[c]
        })
      }
    }

    //子父节点添加关联
    top.children.push(element)
    element.parent = top;

    // cssCompute.computeCSS(element,stack)

    if(!token.isSelfClose){
      stack.push(element);
    } 
    currentTextNode = null;

  } else if(token.type === "endTag"){
    if(top.tagName !== token.tagName){
      console.log(token)
      throw new Error("Tag start")
    }else {
      // 遇到<style></style>即可进行css收集
      // if(top.tagName === "style"){
      //   cssCompute.addCssRules(top.children[0].content)
      // }

      //遇到endTag计算layout
      // layout(top)

      //遇到endTag出栈
      stack.pop();
    }
    currentTextNode = null

  } else if(token.type == "text"){ //文本节点处理

    if(!currentTextNode){
      currentTextNode = {
        type: "text",
        content: ""
      }
    }

    top.children.push(currentTextNode);
    currentTextNode.content += token.content;
  }
}

const EOF = Symbol("EOF"); //EOF：end of file

function data(c){
  if(c === "<"){
    return tagOpen;
  } else if(c == EOF){
    emit({
      type: "EOF"
    })
    return ;
  } else {
    emit({
      type: "text",
      content: c
    })
    return data;
  }
}

function tagOpen(c){
  if(c === '/'){
    return endTagOpen;
  } else if(c.match(/^[a-zA-Z]$/)){
    currentToken = {
      type: "startTag",
      tagName: ""
    }
    return tagName(c);
  }  else {
    return ;
  }
}

function endTagOpen(c){
  if(c.match(/^[a-zA-Z]$/)){
    currentToken = {
      type: "endTag",
      tagName: ""
    }
    return tagName(c);
  } else if(c === '>'){
    
  } else if(c == EOF){
    emit({
      type: "EOF"
    })
    return;
  } else {
  }
}

function tagName(c){
  if(c.match(/^[\t\n\f ]$/)) {
    return beforeAttributeName;
  } else if(c === "/"){
    return selfClosingStartTag;
  } else if(c.match(/^[a-zA-Z]$/)){
    currentToken.tagName += c;
    return tagName;
  } else if(c ===">"){
    emit(currentToken);
    return data;
  } else {
    currentToken.tagName += c;
    return tagName;
  }
}

function beforeAttributeName(c){
  if(c.match(/^[\t\n\f ]$/)) {
    return beforeAttributeName;
  } else if(c ===">" || c==="/" || c== EOF){
    return afterAttributeName(c);
  }else if(c === "="){
    
  } 
  else{
    currentAttribute = {
      name:"",
      value: ""
    }
    return attributeName(c);
  } 
}

function attributeName(c){
  if(c.match(/^[\t\n\f ]$/)) {
    return afterAttributeName;
  } else if(c == "="){
    return beforeAttributeValue;
  } else if(c == "\u0000"){

  } else if(c == "\"" || c == "'" || c== "<"){

  } else {
    currentAttribute.name += c;
    return attributeName;
  }
}

function afterAttributeName(c){
  if(c.match(/^[\t\n\f ]$/)) {
    return afterAttributeName;
  } else if(c == "="){
    return beforeAttributeValue;
  } else if(c == "/"){
    return selfClosingStartTag;
  } else if(c == ">"){
    currentToken[currentAttribute.name] = currentAttribute.value;
    emit(currentToken);
    return data;
  } else if(c == EOF){

  } else {
    currentToken[currentAttribute.name] = currentAttribute.value;
    currentAttribute = {
      name: "",
      value: ""
    }
    return attributeName(c);
  }
}

function beforeAttributeValue(c){
  if(c.match(/^[\t\n\f ]$/) || c == "/" || c == ">" || c == EOF) {
    return beforeAttributeValue;
  } else if (c == "\""){
    return doubleQuotedAttributeValue;
  } else if (c == "\'"){
    return singleQuotedAttributeValue;
  } else if (c == ">"){
    
  } else {
    return UnquotedAttributeValue;
  }
}

function doubleQuotedAttributeValue(c){
  if(c == "\""){
    currentToken[currentAttribute.name] = currentAttribute.value;
    return afterQuotedAttribute;
  } else if (c == "\u0000"){

  } else if(c == EOF){

  } else {
    currentAttribute.value += c;
    return doubleQuotedAttributeValue;
  }
}

function singleQuotedAttributeValue(c){
  if(c == "\'"){
    currentToken[currentAttribute.name] = currentAttribute.value;
    return afterQuotedAttribute;
  } else if (c == "\u0000"){

  } else if(c == EOF){

  } else {
    currentAttribute.value += c;
    return singleQuotedAttributeValue;
  }
}

function afterQuotedAttribute(c){
  if(c.match(/^[\t\n\f ]$/)){
    return beforeAttributeName;
  } else if(c == "/"){
    return selfClosingStartTag;
  } else if(c == ">"){
    currentToken[currentAttribute.name] = currentAttribute.value;
    emit(currentToken);
    return data;
  }else if(c == EOF){

  } else {
    // ?????
    currentAttribute.value += c;
    return doubleQuotedAttributeValue;
  }
}

function UnquotedAttributeValue(c){
  if(c.match(/^[\t\f\n ]$/)){
    currentToken[currentAttribute.name] = currentAttribute.value;
    return beforeAttributeName;
  } else if(c =="/"){
    currentToken[currentAttribute.name] = currentAttribute.value;
    return selfClosingStartTag;
  } else if(c == ">"){
    currentToken[currentAttribute.name] = currentAttribute.value;
    emit(currentToken);
    return data;
  } else if(c == "\u0000"){
    
  } else if(c == "\"" || c =="'" || c =="<" || c == "=" || c =="`"){
    
  } else if(c == EOF){
    
  } else {
    currentAttribute.value += c;
    return UnquotedAttributeValue;
  }
}

function selfClosingStartTag(c){
  if(c === ">"){
    currentToken.isSelfClose = true;
    emit(currentToken);
    return data;
  } else if(c == "EOF"){

  } else {

  }
}



module.exports.parseHtml = function parseHtml(html){
  // console.log(html)
  stack = [{type:'document',children:[]}]
  currentToken = null;
  currentAttribute = null;
  currentTextNode = null;
  let state = data;
  for(let c of html){
    state = state(c)
  }
  state = state(EOF);
  console.log(stack[0])
  return stack[0];
}